<template>
  <div class="file-container">
    <el-card>
      <!-- 上传区域 -->
      <div class="upload-section">
        <el-tabs v-model="activeTab">
          <el-tab-pane label="普通上传" name="normal">
            <el-upload
              class="upload-demo"
              drag
              action=""
              :http-request="handleNormalUpload"
              :before-upload="beforeUpload"
              :on-success="handleUploadSuccess"
              :on-error="handleUploadError"
              multiple
            >
              <i class="el-icon-upload"></i>
              <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
              <div class="el-upload__tip" slot="tip">支持任意格式文件上传，单个文件不超过100MB</div>
            </el-upload>
          </el-tab-pane>

          <el-tab-pane label="分片上传" name="chunk">
            <div class="chunk-upload">
              <el-upload
                class="upload-demo"
                drag
                action=""
                :http-request="handleChunkUpload"
                :before-upload="beforeUpload"
                :show-file-list="false"
              >
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">将大文件拖到此处，或<em>点击上传</em></div>
                <div class="el-upload__tip" slot="tip">适用于大文件上传，支持断点续传</div>
              </el-upload>
              
              <div v-if="uploadProgress > 0 && uploadProgress < 100" class="progress-section">
                <el-progress :percentage="uploadProgress" :stroke-width="20"></el-progress>
                <div class="upload-info">
                  <p>正在上传: {{ currentFileName }}</p>
                  <p>上传速度: {{ uploadSpeed }}</p>
                  <p>剩余时间: {{ remainingTime }}</p>
                </div>
                <el-button type="danger" size="small" @click="cancelUpload" style="margin-top: 10px;">取消上传</el-button>
              </div>
            </div>
          </el-tab-pane>
        </el-tabs>
      </div>

      <!-- 搜索区域 -->
      <el-form :inline="true" :model="searchForm" class="search-form">
        <el-form-item label="文件名">
          <el-input v-model="searchForm.fileName" placeholder="请输入文件名" clearable></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
          <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>

      <!-- 文件列表 -->
      <el-table :data="tableData" border stripe v-loading="loading">
        <el-table-column prop="id" label="ID" width="80"></el-table-column>
        <el-table-column prop="fileName" label="文件名" show-overflow-tooltip></el-table-column>
        <el-table-column prop="fileType" label="类型" width="100"></el-table-column>
        <el-table-column prop="fileSize" label="大小" width="120">
          <template slot-scope="scope">
            {{ formatFileSize(scope.row.fileSize) }}
          </template>
        </el-table-column>
        <el-table-column prop="md5" label="MD5" width="250" show-overflow-tooltip></el-table-column>
        <el-table-column prop="createTime" label="上传时间" width="180"></el-table-column>
        <el-table-column label="操作" width="150" fixed="right">
          <template slot-scope="scope">
            <el-button type="primary" size="small" @click="handleDownload(scope.row)">下载</el-button>
            <el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>

      <!-- 分页 -->
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pagination.current"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pagination.size"
        :total="pagination.total"
        layout="total, sizes, prev, pager, next, jumper"
        style="margin-top: 20px; text-align: right;"
      ></el-pagination>
    </el-card>
  </div>
</template>

<script>
import { getFilePage, uploadFile, uploadChunk, mergeChunks, checkFile, checkUploadedChunks, deleteFile } from '@/api/file'
import { formatFileSize } from '@/utils/common'
import md5 from 'js-md5'

export default {
  name: 'FileManage',
  data() {
    return {
      activeTab: 'normal',
      searchForm: {
        fileName: ''
      },
      tableData: [],
      pagination: {
        current: 1,
        size: 10,
        total: 0
      },
      loading: false,
      uploadProgress: 0,
      currentFileName: '',
      chunkSize: 5 * 1024 * 1024, // 5MB
      uploadSpeed: '0 KB/s',
      remainingTime: '--',
      startTime: 0,
      uploadedSize: 0,
      cancelToken: null
    }
  },
  created() {
    this.loadData()
  },
  methods: {
    formatFileSize,
    
    loadData() {
      this.loading = true
      const params = {
        current: this.pagination.current,
        size: this.pagination.size,
        ...this.searchForm
      }
      getFilePage(params).then(res => {
        this.tableData = res.data.records
        this.pagination.total = res.data.total
        this.loading = false
      }).catch(() => {
        this.loading = false
      })
    },
    
    beforeUpload(file) {
      const isLt100M = file.size / 1024 / 1024 < 100
      if (!isLt100M) {
        this.$message.error('文件大小不能超过 100MB!')
        return false
      }
      return true
    },
    
    // 普通上传
    handleNormalUpload(params) {
      const formData = new FormData()
      formData.append('file', params.file)
      
      uploadFile(formData).then(res => {
        this.$message.success('上传成功')
        this.loadData()
      }).catch(() => {
        this.$message.error('上传失败')
      })
    },
    
    // 分片上传
    async handleChunkUpload(params) {
      const file = params.file
      this.currentFileName = file.name
      this.uploadProgress = 0
      this.uploadSpeed = '0 KB/s'
      this.remainingTime = '--'
      this.startTime = Date.now()
      this.uploadedSize = 0
      this.cancelToken = { cancelled: false }
      
      try {
        // 计算文件MD5
        this.$message.info('正在计算文件MD5...')
        const fileMd5 = await this.calculateFileMD5(file)
        
        // 检查文件是否已存在（秒传）
        const checkResult = await checkFile(fileMd5)
        if (checkResult.data) {
          this.$message.success('文件已存在，秒传成功')
          this.uploadProgress = 100
          setTimeout(() => {
            this.uploadProgress = 0
            this.loadData()
          }, 1000)
          return
        }
        
        // 检查是否有未完成的分片（断点续传）
        const uploadedChunksResult = await checkUploadedChunks(fileMd5)
        const startChunk = uploadedChunksResult.data || 0
        
        if (startChunk > 0) {
          this.$message.info(`检测到未完成的上传，从第 ${startChunk + 1} 个分片开始续传`)
        }
        
        // 分片上传
        const totalChunks = Math.ceil(file.size / this.chunkSize)
        
        for (let i = startChunk; i < totalChunks; i++) {
          // 检查是否取消
          if (this.cancelToken.cancelled) {
            this.$message.warning('上传已取消')
            this.resetUploadState()
            return
          }
          
          const start = i * this.chunkSize
          const end = Math.min(start + this.chunkSize, file.size)
          const chunk = file.slice(start, end)
          
          const formData = new FormData()
          formData.append('chunk', chunk)
          formData.append('md5', fileMd5)
          formData.append('chunkNumber', i + 1)
          formData.append('totalChunks', totalChunks)
          
          await uploadChunk(formData)
          
          // 更新进度和速度
          this.uploadedSize += chunk.size
          const progress = Math.floor(((i + 1) / totalChunks) * 100)
          this.uploadProgress = progress
          this.updateUploadSpeed(file.size)
        }
        
        // 合并分片
        this.$message.info('正在合并文件...')
        await mergeChunks({
          md5: fileMd5,
          fileName: file.name,
          totalChunks: totalChunks
        })
        
        this.$message.success('上传成功')
        this.resetUploadState()
        this.loadData()
      } catch (error) {
        if (!this.cancelToken.cancelled) {
          this.$message.error('上传失败')
        }
        this.resetUploadState()
      }
    },
    
    // 更新上传速度
    updateUploadSpeed(totalSize) {
      const elapsed = (Date.now() - this.startTime) / 1000 // 秒
      const speed = this.uploadedSize / elapsed // 字节/秒
      
      // 格式化速度
      if (speed < 1024) {
        this.uploadSpeed = speed.toFixed(2) + ' B/s'
      } else if (speed < 1024 * 1024) {
        this.uploadSpeed = (speed / 1024).toFixed(2) + ' KB/s'
      } else {
        this.uploadSpeed = (speed / 1024 / 1024).toFixed(2) + ' MB/s'
      }
      
      // 计算剩余时间
      const remaining = totalSize - this.uploadedSize
      const remainingSeconds = remaining / speed
      
      if (remainingSeconds < 60) {
        this.remainingTime = Math.ceil(remainingSeconds) + ' 秒'
      } else if (remainingSeconds < 3600) {
        this.remainingTime = Math.ceil(remainingSeconds / 60) + ' 分钟'
      } else {
        this.remainingTime = Math.ceil(remainingSeconds / 3600) + ' 小时'
      }
    },
    
    // 取消上传
    cancelUpload() {
      this.cancelToken.cancelled = true
      this.$message.warning('正在取消上传...')
    },
    
    // 重置上传状态
    resetUploadState() {
      this.uploadProgress = 0
      this.uploadSpeed = '0 KB/s'
      this.remainingTime = '--'
      this.uploadedSize = 0
      this.cancelToken = null
    },
    
    // 计算文件MD5
    calculateFileMD5(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsArrayBuffer(file)
        reader.onload = (e) => {
          const hash = md5(e.target.result)
          resolve(hash)
        }
        reader.onerror = (e) => {
          reject(e)
        }
      })
    },
    
    handleUploadSuccess() {
      this.$message.success('上传成功')
      this.loadData()
    },
    
    handleUploadError() {
      this.$message.error('上传失败')
    },
    
    handleDownload(row) {
      window.open(`/api/file/download/${row.id}`, '_blank')
    },
    
    handleDelete(row) {
      this.$confirm('确定删除该文件吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteFile(row.id).then(() => {
          this.$message.success('删除成功')
          this.loadData()
        })
      })
    },
    
    handleSearch() {
      this.pagination.current = 1
      this.loadData()
    },
    
    handleReset() {
      this.searchForm.fileName = ''
      this.handleSearch()
    },
    
    handleSizeChange(val) {
      this.pagination.size = val
      this.loadData()
    },
    
    handleCurrentChange(val) {
      this.pagination.current = val
      this.loadData()
    }
  }
}
</script>

<style scoped>
.file-container {
  padding: 20px;
}

.upload-section {
  margin-bottom: 30px;
}

.chunk-upload {
  position: relative;
}

.progress-section {
  margin-top: 20px;
  padding: 20px;
  background: #f5f7fa;
  border-radius: 4px;
}

.upload-info {
  margin-top: 15px;
}

.upload-info p {
  margin: 5px 0;
  color: #606266;
  font-size: 14px;
}

.progress-section p {
  margin-top: 10px;
  text-align: center;
  color: #606266;
}

.search-form {
  margin-bottom: 20px;
}

.upload-demo {
  width: 100%;
}
</style>
